Skip to content

fix: composite primary key tables update/delete wrong rows#742

Merged
datlechin merged 4 commits intomainfrom
fix/composite-primary-key-editing
Apr 14, 2026
Merged

fix: composite primary key tables update/delete wrong rows#742
datlechin merged 4 commits intomainfrom
fix/composite-primary-key-editing

Conversation

@datlechin
Copy link
Copy Markdown
Collaborator

Summary

Fixes editing in tables with composite primary keys. Previously, UPDATE and DELETE statements only used the first PK column in the WHERE clause, causing all rows sharing that value to be affected instead of just the target row.

Root cause: The entire editing pipeline modeled primary keys as primaryKeyColumn: String? (single column). Schema detection used .first(where: { $0.isPrimaryKey }) which silently dropped all but the first PK column.

Changes

Composite PK support (15 source files)

Thread primaryKeyColumns: [String] through the pipeline, replacing primaryKeyColumn: String?:

  • TableSchemaprimaryKeyColumns: [String], computed primaryKeyColumn for backward compat
  • QueryTab, TabPendingChanges — same rename
  • DataChangeManagerprimaryKeyColumns: [String] + configureForTable signature
  • SQLStatementGenerator — UPDATE: loops all PK columns → WHERE a = ? AND b = ?; batch DELETE: (a = ? AND b = ?) OR (a = ? AND b = ?)
  • MainContentCoordinator+QueryHelpers.filter { $0.isPrimaryKey }.map(\.name) instead of .first(where:)?.name
  • DataGridView, DataGridCoordinatorprimaryKeyColumns: [String] with computed primaryKeyColumn
  • RowOperationsManager — sets __DEFAULT__ on all PK columns when duplicating rows

Structure view safe mode bypass (1 file)

TableStructureView+Schema.swiftexecuteSchemaChanges() now checks connection.safeModeLevel.blocksAllWrites before executing DDL

discardChanges() stale state (1 file)

DataChangeManager.swift — added missing changedRowIndices.removeAll()

Tests (14 files)

  • New: SQLStatementGeneratorCompositePKTests.swift — 22 test cases
  • Updated: 13 existing test files for primaryKeyColumns: [String] parameter rename

Test coverage (22 new tests)

Category Count Scenarios
UPDATE + composite PK 6 2-col, 3-col, parameter order, multi-cell, PK column edit, multiple rows
UPDATE + dialects 2 PostgreSQL $N, MSSQL [bracket]
DELETE + composite PK 3 Single row, batch, PostgreSQL batch
Single PK regression 2 UPDATE, batch DELETE
No PK fallback 3 UPDATE all-column, DELETE individual, NULL → IS NULL
Edge cases 6 NULL PK skip, no originalRow fallback, mixed ops

How to reproduce the original bug

CREATE TABLE order_items (
    order_id INT, product_id INT, quantity INT,
    PRIMARY KEY (order_id, product_id)
);
INSERT INTO order_items VALUES (1,100,2), (1,200,3), (1,300,1);

Edit quantity of row (1, 200) → Save → Before fix: all 3 rows updated. After fix: only (1, 200) updated.

Test plan

  • Create composite PK table, edit one row → verify only that row updates
  • Delete one row in composite PK table → verify only that row deleted
  • Preview SQL (Cmd+Shift+P) → verify WHERE has all PK columns with AND
  • Verify single-PK tables still work correctly
  • Verify no-PK tables use all-column fallback
  • Set connection to read-only → open Structure view → try Save → verify blocked
  • Discard changes → verify no stale row highlighting

Thread primaryKeyColumns: [String] through the entire editing pipeline,
replacing primaryKeyColumn: String? which silently dropped all but the
first PK column. UPDATE and DELETE now use all PK columns in WHERE.

Also: structure view saves now respect safe mode on read-only
connections, and discardChanges() clears stale changedRowIndices.
The .onChange handler for column changes unconditionally set
primaryKeyColumns to [firstColumn], ignoring the actual schema PKs.
Now reads from tab.primaryKeyColumns which preserves the full
composite PK set from schema detection.
PRAGMA table_info returns pk as position (1, 2, 3...) not boolean.
The check row[5] == "1" only matched the first PK column. Changed
to row[5] != "0" to detect all columns in a composite primary key.
@datlechin datlechin merged commit 1dce3e1 into main Apr 14, 2026
2 checks passed
@datlechin datlechin deleted the fix/composite-primary-key-editing branch April 14, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant